!**********************************************************************************************************************************
! LICENSING
! Copyright (C) 2015-2016  National Renewable Energy Laboratory
!
!    This file is part of InflowWind.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
!     http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!
!**********************************************************************************************************************************
MODULE InflowWind_Subs

   USE InflowWind_Types
   USE NWTC_Library
   USE IfW_FlowField, only: IfW_FlowField_GetVelAcc

   IMPLICIT NONE



! ===================================================================================================
! NOTE: The following lines of code were generated by a Matlab script called "Write_ChckOutLst.m"
!      using the parameters listed in the "OutListParameters.xlsx" Excel file. Any changes to these
!      lines should be modified in the Matlab script and/or Excel worksheet as necessary.
! ===================================================================================================
! This code was generated by "Write_ChckOutLst.m" at 05-Apr-2023.


     ! Indices for computing output channels:
     ! NOTES:
     !    (1) These parameters are in the order stored in "OutListParameters.xlsx"
     !    (2) Array AllOuts() must be dimensioned to the value of the largest output parameter

     !  Time:

   INTEGER(IntKi), PARAMETER      :: Time           =   0


     ! Wind Motions:

   INTEGER(IntKi), PARAMETER      :: Wind1VelX      =   1
   INTEGER(IntKi), PARAMETER      :: Wind1VelY      =   2
   INTEGER(IntKi), PARAMETER      :: Wind1VelZ      =   3
   INTEGER(IntKi), PARAMETER      :: Wind2VelX      =   4
   INTEGER(IntKi), PARAMETER      :: Wind2VelY      =   5
   INTEGER(IntKi), PARAMETER      :: Wind2VelZ      =   6
   INTEGER(IntKi), PARAMETER      :: Wind3VelX      =   7
   INTEGER(IntKi), PARAMETER      :: Wind3VelY      =   8
   INTEGER(IntKi), PARAMETER      :: Wind3VelZ      =   9
   INTEGER(IntKi), PARAMETER      :: Wind4VelX      =  10
   INTEGER(IntKi), PARAMETER      :: Wind4VelY      =  11
   INTEGER(IntKi), PARAMETER      :: Wind4VelZ      =  12
   INTEGER(IntKi), PARAMETER      :: Wind5VelX      =  13
   INTEGER(IntKi), PARAMETER      :: Wind5VelY      =  14
   INTEGER(IntKi), PARAMETER      :: Wind5VelZ      =  15
   INTEGER(IntKi), PARAMETER      :: Wind6VelX      =  16
   INTEGER(IntKi), PARAMETER      :: Wind6VelY      =  17
   INTEGER(IntKi), PARAMETER      :: Wind6VelZ      =  18
   INTEGER(IntKi), PARAMETER      :: Wind7VelX      =  19
   INTEGER(IntKi), PARAMETER      :: Wind7VelY      =  20
   INTEGER(IntKi), PARAMETER      :: Wind7VelZ      =  21
   INTEGER(IntKi), PARAMETER      :: Wind8VelX      =  22
   INTEGER(IntKi), PARAMETER      :: Wind8VelY      =  23
   INTEGER(IntKi), PARAMETER      :: Wind8VelZ      =  24
   INTEGER(IntKi), PARAMETER      :: Wind9VelX      =  25
   INTEGER(IntKi), PARAMETER      :: Wind9VelY      =  26
   INTEGER(IntKi), PARAMETER      :: Wind9VelZ      =  27
   INTEGER(IntKi), PARAMETER      :: WindHubVelX    =  28
   INTEGER(IntKi), PARAMETER      :: WindHubVelY    =  29
   INTEGER(IntKi), PARAMETER      :: WindHubVelZ    =  30
   INTEGER(IntKi), PARAMETER      :: WindDiskVelX   =  31
   INTEGER(IntKi), PARAMETER      :: WindDiskVelY   =  32
   INTEGER(IntKi), PARAMETER      :: WindDiskVelZ   =  33


     ! Wind Accelerations:

   INTEGER(IntKi), PARAMETER      :: Wind1AccX      =  34
   INTEGER(IntKi), PARAMETER      :: Wind1AccY      =  35
   INTEGER(IntKi), PARAMETER      :: Wind1AccZ      =  36
   INTEGER(IntKi), PARAMETER      :: Wind2AccX      =  37
   INTEGER(IntKi), PARAMETER      :: Wind2AccY      =  38
   INTEGER(IntKi), PARAMETER      :: Wind2AccZ      =  39
   INTEGER(IntKi), PARAMETER      :: Wind3AccX      =  40
   INTEGER(IntKi), PARAMETER      :: Wind3AccY      =  41
   INTEGER(IntKi), PARAMETER      :: Wind3AccZ      =  42
   INTEGER(IntKi), PARAMETER      :: Wind4AccX      =  43
   INTEGER(IntKi), PARAMETER      :: Wind4AccY      =  44
   INTEGER(IntKi), PARAMETER      :: Wind4AccZ      =  45
   INTEGER(IntKi), PARAMETER      :: Wind5AccX      =  46
   INTEGER(IntKi), PARAMETER      :: Wind5AccY      =  47
   INTEGER(IntKi), PARAMETER      :: Wind5AccZ      =  48
   INTEGER(IntKi), PARAMETER      :: Wind6AccX      =  49
   INTEGER(IntKi), PARAMETER      :: Wind6AccY      =  50
   INTEGER(IntKi), PARAMETER      :: Wind6AccZ      =  51
   INTEGER(IntKi), PARAMETER      :: Wind7AccX      =  52
   INTEGER(IntKi), PARAMETER      :: Wind7AccY      =  53
   INTEGER(IntKi), PARAMETER      :: Wind7AccZ      =  54
   INTEGER(IntKi), PARAMETER      :: Wind8AccX      =  55
   INTEGER(IntKi), PARAMETER      :: Wind8AccY      =  56
   INTEGER(IntKi), PARAMETER      :: Wind8AccZ      =  57
   INTEGER(IntKi), PARAMETER      :: Wind9AccX      =  58
   INTEGER(IntKi), PARAMETER      :: Wind9AccY      =  59
   INTEGER(IntKi), PARAMETER      :: Wind9AccZ      =  60


     ! Wind Magnitude and Direction:

   INTEGER(IntKi), PARAMETER      :: Wind1VelXY     =  61
   INTEGER(IntKi), PARAMETER      :: Wind2VelXY     =  62
   INTEGER(IntKi), PARAMETER      :: Wind3VelXY     =  63
   INTEGER(IntKi), PARAMETER      :: Wind4VelXY     =  64
   INTEGER(IntKi), PARAMETER      :: Wind5VelXY     =  65
   INTEGER(IntKi), PARAMETER      :: Wind6VelXY     =  66
   INTEGER(IntKi), PARAMETER      :: Wind7VelXY     =  67
   INTEGER(IntKi), PARAMETER      :: Wind8VelXY     =  68
   INTEGER(IntKi), PARAMETER      :: Wind9VelXY     =  69
   INTEGER(IntKi), PARAMETER      :: WindHubVelXY   =  70
   INTEGER(IntKi), PARAMETER      :: WindDiskVelXY  =  71
   INTEGER(IntKi), PARAMETER      :: Wind1VelMag    =  72
   INTEGER(IntKi), PARAMETER      :: Wind2VelMag    =  73
   INTEGER(IntKi), PARAMETER      :: Wind3VelMag    =  74
   INTEGER(IntKi), PARAMETER      :: Wind4VelMag    =  75
   INTEGER(IntKi), PARAMETER      :: Wind5VelMag    =  76
   INTEGER(IntKi), PARAMETER      :: Wind6VelMag    =  77
   INTEGER(IntKi), PARAMETER      :: Wind7VelMag    =  78
   INTEGER(IntKi), PARAMETER      :: Wind8VelMag    =  79
   INTEGER(IntKi), PARAMETER      :: Wind9VelMag    =  80
   INTEGER(IntKi), PARAMETER      :: WindHubVelMag  =  81
   INTEGER(IntKi), PARAMETER      :: WindDiskVelMag =  82
   INTEGER(IntKi), PARAMETER      :: Wind1AngXY     =  83
   INTEGER(IntKi), PARAMETER      :: Wind2AngXY     =  84
   INTEGER(IntKi), PARAMETER      :: Wind3AngXY     =  85
   INTEGER(IntKi), PARAMETER      :: Wind4AngXY     =  86
   INTEGER(IntKi), PARAMETER      :: Wind5AngXY     =  87
   INTEGER(IntKi), PARAMETER      :: Wind6AngXY     =  88
   INTEGER(IntKi), PARAMETER      :: Wind7AngXY     =  89
   INTEGER(IntKi), PARAMETER      :: Wind8AngXY     =  90
   INTEGER(IntKi), PARAMETER      :: Wind9AngXY     =  91
   INTEGER(IntKi), PARAMETER      :: WindHubAngXY   =  92
   INTEGER(IntKi), PARAMETER      :: WindDiskAngXY  =  93


     ! Wind Sensor Measurements:

   INTEGER(IntKi), PARAMETER      :: WindMeas1      =  94
   INTEGER(IntKi), PARAMETER      :: WindMeas2      =  95
   INTEGER(IntKi), PARAMETER      :: WindMeas3      =  96
   INTEGER(IntKi), PARAMETER      :: WindMeas4      =  97
   INTEGER(IntKi), PARAMETER      :: WindMeas5      =  98


     ! The maximum number of output channels which can be output by the code.
   INTEGER(IntKi), PARAMETER      :: MaxOutPts      =  98

!End of code generated by Matlab script Write_ChckOutLst
! ===================================================================================================

   
   INTEGER(IntKi), PARAMETER      :: WindMeas(5) = (/ WindMeas1, WindMeas2, WindMeas3, WindMeas4, WindMeas5 /)                                               ! Array of output constants

   INTEGER(IntKi), PARAMETER      :: WindVelX(9) = (/ Wind1VelX, Wind2VelX, Wind3VelX, Wind4VelX, Wind5VelX, Wind6VelX, Wind7VelX, Wind8VelX, Wind9VelX /)   ! Array of output constants
   INTEGER(IntKi), PARAMETER      :: WindVelY(9) = (/ Wind1VelY, Wind2VelY, Wind3VelY, Wind4VelY, Wind5VelY, Wind6VelY, Wind7VelY, Wind8VelY, Wind9VelY /)   ! Array of output constants
   INTEGER(IntKi), PARAMETER      :: WindVelZ(9) = (/ Wind1VelZ, Wind2VelZ, Wind3VelZ, Wind4VelZ, Wind5VelZ, Wind6VelZ, Wind7VelZ, Wind8VelZ, Wind9VelZ /)   ! Array of output constants
   
   INTEGER(IntKi), PARAMETER      :: WindAccX(9) = (/ Wind1AccX, Wind2AccX, Wind3AccX, Wind4AccX, Wind5AccX, Wind6AccX, Wind7AccX, Wind8AccX, Wind9AccX /)   ! Array of output constants
   INTEGER(IntKi), PARAMETER      :: WindAccY(9) = (/ Wind1AccY, Wind2AccY, Wind3AccY, Wind4AccY, Wind5AccY, Wind6AccY, Wind7AccY, Wind8AccY, Wind9AccY /)   ! Array of output constants
   INTEGER(IntKi), PARAMETER      :: WindAccZ(9) = (/ Wind1AccZ, Wind2AccZ, Wind3AccZ, Wind4AccZ, Wind5AccZ, Wind6AccZ, Wind7AccZ, Wind8AccZ, Wind9AccZ /)   ! Array of output constants
   
   
   INTEGER(IntKi), PARAMETER      :: WindVelXY( 9) = (/ Wind1VelXY,  Wind2VelXY,  Wind3VelXY,  Wind4VelXY,  Wind5VelXY,  Wind6VelXY,  Wind7VelXY,  Wind8VelXY,  Wind9VelXY /)   ! Array of output constants
   INTEGER(IntKi), PARAMETER      :: WindVelMag(9) = (/ Wind1VelMag, Wind2VelMag, Wind3VelMag, Wind4VelMag, Wind5VelMag, Wind6VelMag, Wind7VelMag, Wind8VelMag, Wind9VelMag/)   ! Array of output constants
   INTEGER(IntKi), PARAMETER      :: WindAngXY( 9) = (/ Wind1AngXY,  Wind2AngXY,  Wind3AngXY,  Wind4AngXY,  Wind5AngXY,  Wind6AngXY,  Wind7AngXY,  Wind8AngXY,  Wind9AngXY /)   ! Array of output constants
   
! ===================================================================================================

CONTAINS

!====================================================================================================
!>  This public subroutine parses the array of strings in InputFileData for the input parameters.
SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, InputFileName, EchoFileName,  FixedWindFileRootName, TurbineID, ErrStat, ErrMsg )
!----------------------------------------------------------------------------------------------------

   IMPLICIT NONE
   CHARACTER(*),              PARAMETER               :: RoutineName="InflowWind_ParseInputFileInfo"

      ! Passed variables
   LOGICAL,                            INTENT(IN   )  :: FixedWindFileRootName!< Do the wind data files have a fixed (DEFAULT) file name? (used by FAST.Farm)
   INTEGER(IntKi),                     INTENT(IN   )  :: TurbineID            !< Wind turbine ID number in the fixed (DEFAULT) file name when FixedWindFileRootName = .TRUE. (used by FAST.Farm)
   TYPE(InflowWind_InputFile),         INTENT(INOUT)  :: InputFileData        !< Data of the InflowWind Input File
   TYPE(FileInfoType),                 INTENT(IN   )  :: InFileInfo           !< The derived type for holding the file information
   CHARACTER(*),                       INTENT(IN   )  :: PriPath              !< Path to InflowWind input files
   CHARACTER(*),                       intent(in   )  :: InputFileName        !< The name of the input file
   CHARACTER(*),                       intent(in   )  :: EchoFileName         !< The name of the echo file, possibly opened in this routine

   INTEGER(IntKi),                     INTENT(  OUT)  :: ErrStat              !< Returned error status from this subroutine
   CHARACTER(*),                       INTENT(  OUT)  :: ErrMsg               !< Returned error message from this subroutine

      ! Local variables
   INTEGER(IntKi)                                     :: CurLine              !< Current entry in InFileInfo%Lines array
   INTEGER(IntKi)                                     :: UnEc                 !< local echo unit

      ! Temoporary messages
   INTEGER(IntKi)                                     :: TmpErrStat
   CHARACTER(ErrMsgLen)                               :: TmpErrMsg

      ! Initialization
   InputFileData%VFlowAngle = 0.0 ! default vertical flow angle
   
   ErrStat                 = ErrID_None
   ErrMsg                  = ""
   InputFileData%EchoFlag  = .FALSE.         ! initialize for error handling (cleanup() routine)
   UnEc                    = -1

      ! Allocate the array for the OutList
   CALL AllocAry( InputFileData%OutList, MaxOutPts, "InflowWind Input File's OutList", TmpErrStat, TmpErrMsg )
   if (Failed()) return

   !---------------------------------------------------------------------------------------------
   ! General settings with wind type, direction and output point list (applies to all wind types)
   !---------------------------------------------------------------------------------------------

   CurLine = 4
   CALL ParseVar( InFileInfo, CurLine, "Echo", InputFileData%EchoFlag, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   if ( InputFileData%EchoFlag ) then
      CALL OpenEcho ( UnEc, TRIM(EchoFileName), TmpErrStat, TmpErrMsg )
         if (Failed()) return;
      WRITE(UnEc, '(A)') 'Echo file for InflowWind input file: '//trim(InputFileName)
      ! Write the first three lines into the echo file
      WRITE(UnEc, '(A)') InFileInfo%Lines(1)
      WRITE(UnEc, '(A)') InFileInfo%Lines(2)
      WRITE(UnEc, '(A)') InFileInfo%Lines(3)

      CurLine = 4
      CALL ParseVar( InFileInfo, CurLine, "Echo", InputFileData%EchoFlag, TmpErrStat, TmpErrMsg, UnEc )
         if (Failed()) return
   endif


   !-------------------------------------------------------------------------------------------------
   !> Read general section with wind type, direction, and output point list (applies to all wind types)
   !-------------------------------------------------------------------------------------------------

      ! switch for wind file type (1=steady; 2=uniform; 3=binary TurbSim FF; 4=binary Bladed-style FF; 5=HAWC format; 6=User defined; 7=native Bladed FF)
   CALL ParseVar( InFileInfo, CurLine, "WindType", InputFileData%WindType, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

      ! Direction of wind propagation (meteorological direction) (deg)
   CALL ParseVar( InFileInfo, CurLine, "PropagationDir", InputFileData%PropagationDir, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

      ! VFlowAngle: Upflow angle (deg)
   CALL ParseVarWDefault( InFileInfo, CurLine, "VFlowAng", InputFileData%VFlowAngle, 0.0_ReKi, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

      ! VelInterpCubic: Velocity interpolation order in time (1=linear; 3=cubic) [Used with WindType=2,3,4,5,7]
   CALL ParseVar( InFileInfo, CurLine, "VelInterpCubic", InputFileData%VelInterpCubic, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

      ! NWindVel: Number of points to output the wind velocity (0 to 9)
   CALL ParseVar( InFileInfo, CurLine, "NWindVel", InputFileData%NWindVel, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

      ! Before proceeding, make sure that NWindVel makes sense
   IF ( InputFileData%NWindVel < 0 .OR. InputFileData%NwindVel > 9 ) THEN
      CALL SetErrStat( ErrID_Fatal, 'NWindVel must be greater than or equal to zero and less than 10.', &
                        ErrStat, ErrMsg, RoutineName )
      CALL CleanUp()
      RETURN
   ELSE

      ! Allocate space for the output location arrays:
      CALL AllocAry( InputFileData%WindVxiList, InputFileData%NWindVel, 'WindVxiList', TmpErrStat, TmpErrMsg )
      if (Failed()) return
      CALL AllocAry( InputFileData%WindVyiList, InputFileData%NWindVel, 'WindVyiList', TmpErrStat, TmpErrMsg )
      if (Failed()) return
      CALL AllocAry( InputFileData%WindVziList, InputFileData%NWindVel, 'WindVziList', TmpErrStat, TmpErrMsg )
      if (Failed()) return
   ENDIF

   CALL ParseAry( InFileInfo, CurLine, 'WindVxiList', InputFileData%WindVxiList, InputFileData%NWindVel, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseAry( InFileInfo, CurLine, 'WindVyiList', InputFileData%WindVyiList, InputFileData%NWindVel, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseAry( InFileInfo, CurLine, 'WindVziList', InputFileData%WindVziList, InputFileData%NWindVel, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for Steady Wind Conditions [used only for WindType = 1]_ section
   !-------------------------------------------------------------------------------------------------

   CurLine = CurLine + 1  ! Skip section break
   CALL ParseVar( InFileInfo, CurLine, "HWindSpeed", InputFileData%Steady_HWindSpeed, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "RefHt", InputFileData%Steady_RefHt, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "PLexp", InputFileData%Steady_PLexp, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for Uniform wind file [used only for WindType = 2]_ section
   !-------------------------------------------------------------------------------------------------

   CurLine = CurLine + 1  ! Skip section break
   CALL ParseVar( InFileInfo, CurLine, "FileName_Uni", InputFileData%Uniform_FileName, TmpErrStat, TmpErrMsg, UnEc, IsPath=.true. )
   if (Failed()) return
   IF ( PathIsRelative( InputFileData%Uniform_FileName ) ) InputFileData%Uniform_FileName = TRIM(PriPath)//TRIM(InputFileData%Uniform_FileName)
   IF ( FixedWindFileRootName ) THEN ! .TRUE. when FAST.Farm uses multiple instances of InflowWind for ambient wind data
      IF ( TurbineID == 0 ) THEN     ! .TRUE. for the FAST.Farm low-resolution domain
         InputFileData%Uniform_FileName = TRIM(InputFileData%Uniform_FileName)//TRIM(PathSep)//'Low.dat'
      ELSE                           ! FAST.Farm high-resolution domain(s)
         InputFileData%Uniform_FileName = TRIM(InputFileData%Uniform_FileName)//TRIM(PathSep)//'HighT'//TRIM(Num2Lstr(TurbineID))//'.dat'
      ENDIF
   ENDIF

   CALL ParseVar( InFileInfo, CurLine, "RefHt_Uni", InputFileData%Uniform_RefHt, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "RefLength", InputFileData%Uniform_RefLength, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for Binary TurbSim Full-Field files [used only for WindType = 3]_ section
   !-------------------------------------------------------------------------------------------------

   CurLine = CurLine + 1  ! Skip section break
   CALL ParseVar( InFileInfo, CurLine, "FileName_BTS", InputFileData%TSFF_FileName, TmpErrStat, TmpErrMsg, UnEc, IsPath=.true. )
   if (Failed()) return
   IF ( PathIsRelative( InputFileData%TSFF_FileName ) ) InputFileData%TSFF_FileName = TRIM(PriPath)//TRIM(InputFileData%TSFF_FileName)
   IF ( FixedWindFileRootName ) THEN ! .TRUE. when FAST.Farm uses multiple instances of InflowWind for ambient wind data
      IF ( TurbineID == 0 ) THEN     ! .TRUE. for the FAST.Farm low-resolution domain
         InputFileData%TSFF_FileName = TRIM(InputFileData%TSFF_FileName)//TRIM(PathSep)//'Low.bts'
      ELSE                           ! FAST.Farm high-resolution domain(s)
         InputFileData%TSFF_FileName = TRIM(InputFileData%TSFF_FileName)//TRIM(PathSep)//'HighT'//TRIM(Num2Lstr(TurbineID))//'.bts'
      ENDIF
   ENDIF

   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for Binary Bladed-style Full-Field files [used only for WindType = 4]_ section
   !-------------------------------------------------------------------------------------------------

   CurLine = CurLine + 1  ! Skip section break
   CALL ParseVar( InFileInfo, CurLine, "FilenameRoot", InputFileData%BladedFF_FileName, TmpErrStat, TmpErrMsg, UnEc, IsPath=.true. )
   if (Failed()) return
   IF ( PathIsRelative( InputFileData%BladedFF_FileName ) ) InputFileData%BladedFF_FileName = TRIM(PriPath)//TRIM(InputFileData%BladedFF_FileName)

   CALL ParseVar( InFileInfo, CurLine, "TowerFile", InputFileData%BladedFF_TowerFile, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for coherent turbulence [used only for WindType = 3 or 4]_ section
   !-------------------------------------------------------------------------------------------------

   ! CurLine = CurLine + 1  ! Skip section break
   ! CALL ParseVar( InFileInfo, CurLine, "CTTS_CoherentTurbFlag", InputFileData%CTTS_CoherentTurb, TmpErrStat, TmpErrMsg, UnEc )
   ! if (Failed()) return

   ! CALL ParseVar( InFileInfo, CurLine, "CTTS_FileName", InputFileData%CTTS_FileName, TmpErrStat, TmpErrMsg, UnEc )
   ! if (Failed()) return
   ! IF ( PathIsRelative( InputFileData%CTTS_FileName ) ) InputFileData%CTTS_FileName = TRIM(PriPath)//TRIM(InputFileData%CTTS_FileName)

   ! CALL ParseVar( InFileInfo, CurLine, "CTTS_Path", InputFileData%CTTS_Path, TmpErrStat, TmpErrMsg, UnEc )
   ! if (Failed()) return
   ! IF ( PathIsRelative( InputFileData%CTTS_Path ) ) InputFileData%CTTS_Path = TRIM(PriPath)//TRIM(InputFileData%CTTS_Path)

   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for HAWC-formatted binary files [used only for WindType = 5]_ section
   !-------------------------------------------------------------------------------------------------

   CurLine = CurLine + 1  ! Skip section break
   CALL ParseVar( InFileInfo, CurLine, "FileName_u", InputFileData%HAWC_FileName_u, TmpErrStat, TmpErrMsg, UnEc, IsPath=.true. )
   if (Failed()) return
   IF ( PathIsRelative( InputFileData%HAWC_FileName_u ) ) InputFileData%HAWC_FileName_u = TRIM(PriPath)//TRIM(InputFileData%HAWC_FileName_u)

   CALL ParseVar( InFileInfo, CurLine, "FileName_v", InputFileData%HAWC_FileName_v, TmpErrStat, TmpErrMsg, UnEc, IsPath=.true. )
   if (Failed()) return
   IF ( PathIsRelative( InputFileData%HAWC_FileName_v ) ) InputFileData%HAWC_FileName_v = TRIM(PriPath)//TRIM(InputFileData%HAWC_FileName_v)

   CALL ParseVar( InFileInfo, CurLine, "FileName_w", InputFileData%HAWC_FileName_w, TmpErrStat, TmpErrMsg, UnEc, IsPath=.true. )
   if (Failed()) return
   IF ( PathIsRelative( InputFileData%HAWC_FileName_w ) ) InputFileData%HAWC_FileName_w = TRIM(PriPath)//TRIM(InputFileData%HAWC_FileName_w)
   
   IF ( FixedWindFileRootName ) THEN ! .TRUE. when FAST.Farm uses multiple instances of InflowWind for ambient wind data
      IF ( TurbineID == 0 ) THEN     ! .TRUE. for the FAST.Farm low-resolution domain
         InputFileData%HAWC_FileName_u = TRIM(InputFileData%HAWC_FileName_u)//TRIM(PathSep)//'Low_u.bin'
         InputFileData%HAWC_FileName_v = TRIM(InputFileData%HAWC_FileName_v)//TRIM(PathSep)//'Low_v.bin'
         InputFileData%HAWC_FileName_w = TRIM(InputFileData%HAWC_FileName_w)//TRIM(PathSep)//'Low_w.bin'
      ELSE                           ! FAST.Farm high-resolution domain(s)
         InputFileData%HAWC_FileName_u = TRIM(InputFileData%HAWC_FileName_u)//TRIM(PathSep)//'HighT'//TRIM(Num2Lstr(TurbineID))//'_u.bin'
         InputFileData%HAWC_FileName_v = TRIM(InputFileData%HAWC_FileName_v)//TRIM(PathSep)//'HighT'//TRIM(Num2Lstr(TurbineID))//'_v.bin'
         InputFileData%HAWC_FileName_w = TRIM(InputFileData%HAWC_FileName_w)//TRIM(PathSep)//'HighT'//TRIM(Num2Lstr(TurbineID))//'_w.bin'
      ENDIF
   ENDIF
   
   CALL ParseVar( InFileInfo, CurLine, "nx", InputFileData%HAWC_nx, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "ny", InputFileData%HAWC_ny, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "nz", InputFileData%HAWC_nz, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "dx", InputFileData%HAWC_dx, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "dy", InputFileData%HAWC_dy, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "dz", InputFileData%HAWC_dz, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "RefHt_HAWC", InputFileData%FF%RefHt, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   !----------------------------------------------------------------------------------------------
   !> Read the _Scaling parameters for turbulence (HAWC-format files) [used only for WindType = 5]_ subsection
   !----------------------------------------------------------------------------------------------

   CurLine = CurLine + 1  ! Skip section break
      ! ScaleMethod: Turbulence scaling method [0=none, 1=direct scaling, 2= calculate scaling factor based on a desired standard deviation]
   CALL ParseVar( InFileInfo, CurLine, "ScaleMethod", InputFileData%FF%ScaleMethod, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "SFx", InputFileData%FF%SF(1), TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "SFy", InputFileData%FF%SF(2), TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "SFz", InputFileData%FF%SF(3), TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "SigmaFx", InputFileData%FF%SigmaF(1), TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "SigmaFy", InputFileData%FF%SigmaF(2), TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "SigmaFz", InputFileData%FF%SigmaF(3), TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   ! CALL ParseVar( InFileInfo, CurLine, "HAWC_TStart", InputFileData%FF%TStart, TmpErrStat, TmpErrMsg, UnEc )
   ! if (Failed()) return

   ! CALL ParseVar( InFileInfo, CurLine, "HAWC_TEnd", InputFileData%FF%TEnd, TmpErrStat, TmpErrMsg, UnEc )
   ! if (Failed()) return

   !----------------------------------------------------------------------------------------------
   !> Read the _Mean wind profile parameters (added to HAWC-format files) [used only for WindType = 5]_ subsection
   !----------------------------------------------------------------------------------------------

   CurLine = CurLine + 1  ! Skip section break
   CALL ParseVar( InFileInfo, CurLine, "URef", InputFileData%FF%URef, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

      ! WindProfileType: Wind profile type (0=constant;1=logarithmic;2=power law)
   CALL ParseVar( InFileInfo, CurLine, "WindProfile", InputFileData%FF%WindProfileType, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "PLExp_HAWC", InputFileData%FF%PLExp, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVar( InFileInfo, CurLine, "Z0", InputFileData%FF%Z0, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   CALL ParseVarWDefault( InFileInfo, CurLine, "XOffset", InputFileData%FF%XOffset, 0.0_ReKi, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return
   
   !-------------------------------------------------------------------------------------------------
   !> Read the _Parameters for LIDAR section
   !------------------------------------------------------------------------------------------------- 
   
  CurLine = CurLine + 1  ! Skip section break
  
      ! LIDAR Sensor Type
   CALL ParseVar( InFileInfo, CurLine, "SensorType", InputFileData%SensorType, TmpErrStat, TmpErrMsg, UnEc )
   IF (Failed()) RETURN

     ! Number of Range Gates
   CALL ParseVar( InFileInfo, CurLine, "NumPulseGate", InputFileData%NumPulseGate, TmpErrStat, TmpErrMsg, UnEc )
   IF (Failed()) RETURN
     
     ! Pulse Gate Spacing
   CALL ParseVar( InFileInfo, CurLine, "PulseSpacing", InputFileData%PulseSpacing, TmpErrStat, TmpErrMsg, UnEc )
   IF (Failed()) RETURN

     ! NumBeam: Number of points to output the lidar measured wind velocity (1 to 5)
   CALL ParseVar( InFileInfo, CurLine, "NumBeam", InputFileData%NumBeam, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

     ! Before proceeding, make sure that NumBeam makes sense for array allocation
   InputFileData%NumBeam = MAX(InputFileData%NumBeam, 1)
   
      ! Allocate space for the output location arrays:
   CALL AllocAry( InputFileData%FocalDistanceX, InputFileData%NumBeam, 'FocalDistanceX', TmpErrStat, TmpErrMsg ); CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   CALL AllocAry( InputFileData%FocalDistanceY, InputFileData%NumBeam, 'FocalDistanceY', TmpErrStat, TmpErrMsg ); CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   CALL AllocAry( InputFileData%FocalDistanceZ, InputFileData%NumBeam, 'FocalDistanceZ', TmpErrStat, TmpErrMsg ); CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
   if (Failed()) return
    
    ! Focal Distance X
   CALL ParseAry( InFileInfo, CurLine, 'FocalDistanceX', InputFileData%FocalDistanceX, InputFileData%NumBeam, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return
 
    ! Focal Distance Y
   CALL ParseAry( InFileInfo, CurLine, 'FocalDistanceY', InputFileData%FocalDistanceY, InputFileData%NumBeam, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

    ! Focal Distance Z
   CALL ParseAry( InFileInfo, CurLine, 'FocalDistanceZ', InputFileData%FocalDistanceZ, InputFileData%NumBeam, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

    ! Rotor Apex Offset Position
   CALL ParseAry( InFileInfo, CurLine, "RotorApexOffsetPos", InputFileData%RotorApexOffsetPos, size(InputFileData%RotorApexOffsetPos), TmpErrStat, TmpErrMsg, UnEc )
   IF (Failed()) RETURN
   
    ! URefIni
   CALL ParseVar( InFileInfo, CurLine, "URefLid", InputFileData%URefLid, TmpErrStat, TmpErrMsg, UnEc )
   IF (Failed()) RETURN 
 
    ! Measurement Interval
   CALL ParseVar( InFileInfo, CurLine, "MeasurementInterval", InputFileData%MeasurementInterval, TmpErrStat, TmpErrMsg, UnEc )
   IF (Failed()) RETURN
   
    ! Lidar Radial Vel
   CALL ParseVar( InFileInfo, CurLine, "LidRadialVel", InputFileData%LidRadialVel, TmpErrStat, TmpErrMsg, UnEc )
   IF (Failed()) RETURN
     
    ! Consider Hub Motion
   CALL ParseVar( InFileInfo, CurLine, "ConsiderHubMotion", InputFileData%ConsiderHubMotion, TmpErrStat, TmpErrMsg, UnEc )
   IF (Failed()) RETURN
   

   !----------------------------------------------------------------------------------------------
   !> Read the _OUTPUT_ subsection
   !----------------------------------------------------------------------------------------------
   CurLine = CurLine + 1  ! Skip section break
   CALL ParseVar( InFileInfo, CurLine, "SumPrint", InputFileData%SumPrint, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   !---------------------- OUTLIST  --------------------------------------------
   CurLine = CurLine + 1  ! Skip comment line
   CALL ReadOutputListFromFileInfo( InFileInfo, CurLine, InputFileData%OutList, InputFileData%NumOuts, TmpErrStat, TmpErrMsg, UnEc )
   if (Failed()) return

   !-------------------------------------------------------------------------------------------------
   ! This is the end of the input file
   !-------------------------------------------------------------------------------------------------

   CALL Cleanup()

   RETURN
   CONTAINS
   !-------------------------------------------------------------------------------------------------
      logical function Failed()
         CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )
         Failed = ErrStat >= AbortErrLev
         if (Failed) CALL Cleanup()
      end function Failed

   !..............................
   SUBROUTINE Cleanup()
      if (UnEc  > -1_IntKi) CLOSE( UnEc )
   END SUBROUTINE Cleanup

END SUBROUTINE InflowWind_ParseInputFileInfo


!====================================================================================================
!> This private subroutine verifies the input required for InflowWind is correctly specified.  This
!! routine checkes all the parameters that are common with all the wind types, then calls subroutines
!! that check the parameters specific to each wind type.  Only the parameters corresponding to the
!! desired wind type are evaluated; the rest are ignored.  Additional checks will be performed after
!! the respective wind file has been read in, but these checks will be performed within the respective
!! wind module.
!
!  The reason for structuring it this way is to allow for relocating the validation routines for the
!  wind type into their respective modules. It might also prove useful later if we change languages
!  but retain the fortran wind modules.
SUBROUTINE InflowWind_ValidateInput( InitInp, InputFileData, ErrStat, ErrMsg )

      IMPLICIT                                           NONE

      CHARACTER(*),              PARAMETER            :: RoutineName="InflowWind_ValidateInput"


      ! Passed variables

   TYPE(InflowWind_InitInputType),     INTENT(IN   )  :: InitInp              !< Input data for initialization
   TYPE(InflowWind_InputFile),         INTENT(INOUT)  :: InputFileData        !< The data for initialization
   INTEGER(IntKi),                     INTENT(  OUT)  :: ErrStat              !< Error status  from this subroutine
   CHARACTER(*),                       INTENT(  OUT)  :: ErrMsg               !< Error message from this subroutine


      ! Local variables
   INTEGER(IntKi)                                     :: I                    !< Generic counter


      ! Initialize ErrStat

   ErrStat = ErrID_None
   ErrMsg  = ""


      !-----------------------------------------------
      ! Data that applies to all wind types
      !-----------------------------------------------

      ! WindType
   IF ( InputFileData%WindType <= Undef_WindNumber .OR. InputFileData%WindType > Highest_WindNumber ) THEN
      CALL SetErrStat(ErrID_Fatal,' Invalid WindType specified.  Only values of '//   &
            TRIM(Num2LStr( Steady_WindNumber        ))//','// &
            TRIM(Num2LStr( Uniform_WindNumber       ))//','// &
            TRIM(Num2LStr( TSFF_WindNumber          ))//','// &
            TRIM(Num2LStr( BladedFF_WindNumber      ))//','// &
            TRIM(Num2LStr( BladedFF_Shr_WindNumber  ))//','// &
            TRIM(Num2LStr( HAWC_WindNumber          ))//', or '// &
            TRIM(Num2LStr( User_WindNumber          ))//' are supported.', &
            ErrStat,ErrMsg,RoutineName)
      RETURN
   ENDIF


   if (InitInp%Linearize) then
      
      if (InputFileData%WindType /= Steady_WindNumber .and. InputFileData%WindType /= Uniform_WindNumber) then
         call SetErrStat(ErrID_Fatal, 'InflowWind can perform linearization on only steady or uniform wind types.', ErrStat, ErrMsg, RoutineName)
         return
      end if
      
      if (InputFileData%SensorType /= SensorType_None) then
         call SetErrStat(ErrID_Fatal, 'InflowWind can not perform linearization with the lidar module enabled.', ErrStat, ErrMsg, RoutineName)
         return
      end if
      
      ! we'd have to redo the math to get this correct, so for now we are disabling upflow for linearization:
      if (.not. EqualRealNos(InputFileData%VFlowAngle, 0.0_ReKi) ) then
         call SetErrStat(ErrID_Fatal, 'VFlowAngle (upflow) must be 0 for linearization analysis in InflowWind.', ErrStat, ErrMsg, RoutineName)
         return
      end if
      
   end if
      
   

      ! make sure that all values for WindVzi are above ground.  Set to 0 otherwise.

   IF ( InitInp%MHK /= MHK_None ) THEN
      DO I = 1, InputFileData%NWindVel
         IF ( InputFileData%WindVziList(I) >= InitInp%WtrDpth + InitInp%MSL2SWL ) THEN
            CALL SetErrStat( ErrID_Warn, ' Requested wind velocity at point ( '//   &
                  TRIM(Num2LStr(InputFileData%WindVxiList(I)))//', '//              &
                  TRIM(Num2LStr(InputFileData%WindVyiList(I)))//', '//              &
                  TRIM(Num2LStr(InputFileData%WindVziList(I)))//') is above MSL. Ignoring this point.', &
                  ErrStat, ErrMsg, RoutineName)
            InputFileData%WindVziList(I)  =  0.0_ReKi
         ENDIF
      ENDDO
   ENDIF

   DO I = 1, InputFileData%NWindVel
      IF ( InputFileData%WindVziList(I) <= 0.0_ReKi ) THEN
         CALL SetErrStat( ErrID_Warn, ' Requested wind velocity at point ( '//   &
               TRIM(Num2LStr(InputFileData%WindVxiList(I)))//', '//              &
               TRIM(Num2LStr(InputFileData%WindVyiList(I)))//', '//              &
               TRIM(Num2LStr(InputFileData%WindVziList(I)))//') is below ground (wind turbine) or below seabed (MHK turbine). Ignoring this point.', &
               ErrStat, ErrMsg, RoutineName)
         InputFileData%WindVziList(I)  =  0.0_ReKi
      ENDIF
   ENDDO


      !-----------------------------------------------
      ! Data specific to a WindType
      !-----------------------------------------------

   SELECT CASE ( InputFileData%WindType )

      CASE ( Steady_WindNumber )
         CALL Steady_ValidateInput()

      CASE ( Uniform_WindNumber )
         CALL Uniform_ValidateInput()

      CASE ( TSFF_WindNumber )
         CALL TSFF_ValidateInput()

      CASE ( BladedFF_WindNumber, BladedFF_Shr_WindNumber )
         CALL BladedFF_ValidateInput()

      CASE ( HAWC_WindNumber )
         CALL HAWC_ValidateInput()

      CASE ( User_WindNumber )
         CALL User_ValidateInput()
         
      CASE ( FDext_WindNumber )
         IF ( .not. InitInp%Use4Dext) call SetErrStat(ErrID_Fatal,'4D external wind file is valid only with FAST.Farm',ErrStat,ErrMsg,RoutineName)
      CASE DEFAULT  ! keep this check to make sure that all new wind types have been accounted for
         CALL SetErrStat(ErrID_Fatal,' Undefined wind type.',ErrStat,ErrMsg,RoutineName)

   END SELECT

      ! NOTE: we are not checking the error status yet, but rather will run through the Coherent turbulence section
      !        before returning.  This way a user will know of errors there as well before having to fix the first
      !        section.

      ! Coherent turbulence
   IF ( InputFileData%CTTS_CoherentTurb ) THEN
      CALL CTTS_ValidateInput()
   ENDIF


   RETURN

CONTAINS

   !> This subroutine checks that the values for the steady wind make sense.  There aren't many to check.
   SUBROUTINE Steady_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="Steady_ValidateInput"


         ! Check that HWindSpeed is positive
      IF ( InputFileData%Steady_HWindSpeed <= 0.0_ReKi ) THEN
         CALL SetErrStat(ErrID_Fatal,' Horizontal wind speed (HWindSpeed) for steady winds must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that RefHt is positive
      IF ( InputFileData%Steady_RefHt <= 0.0_ReKi ) THEN
         CALL SetErrStat(ErrID_Fatal,' Reference height (RefHt) for steady winds must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that PLexp is positive
      IF ( InputFileData%Steady_PLexp < 0.0_ReKi ) THEN
         CALL SetErrStat(ErrID_Fatal,' Power law exponent (PLexp) for steady winds must be positive or zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      RETURN
   END SUBROUTINE Steady_ValidateInput



   !> This subroutine checks that the values for the uniform wind make sense.  There isn't much to check.
   SUBROUTINE Uniform_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="Uniform_ValidateInput"

         ! Local variables
      LOGICAL                                         :: TmpFileExist

         ! Check that the filename requested actually exists
      INQUIRE( file=InputFileData%Uniform_FileName, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find uniform wind input file: '"//TRIM(InputFileData%Uniform_FileName)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that RefHt is positive
      IF ( InputFileData%Uniform_RefHt <= 0.0_ReKi ) THEN
         CALL SetErrStat(ErrID_Fatal,' Reference height (RefHt) for uniform winds must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that RefLength is positive
      IF ( InputFileData%Uniform_RefLength <= 0.0_ReKi ) THEN
         CALL SetErrStat(ErrID_Fatal,' Reference length (RefLength) for uniform winds must be greater than zero.',  &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      RETURN

   END SUBROUTINE Uniform_ValidateInput



   !> There isn't much to check for the TurbSim full-field section of the input file.
   SUBROUTINE TSFF_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="TSFF_ValidateInput"

         ! Local variables
      LOGICAL                                         :: TmpFileExist

         ! Check that the filename requested actually exists
      INQUIRE( file=InputFileData%TSFF_FileName, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find TurbSim full-field wind input file: '"//TRIM(InputFileData%TSFF_FileName)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      RETURN

   END SUBROUTINE TSFF_ValidateInput



   !> We will only check the main bladed input file here.  We will check the existance of the towerfile (if requested) in the SetParameters routine.
   SUBROUTINE BladedFF_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="BladedFF_ValidateInput"

         ! Local variables
      LOGICAL                                         :: TmpFileExist

      !   ! Check that the filename requested actually exists (this happens in the BladedFF submodule...)
      !INQUIRE( file=InputFileData%BladedFF_FileName, exist=TmpFileExist )
      !IF ( .NOT. TmpFileExist ) THEN
      !   CALL SetErrStat( ErrID_Fatal," Cannot find Bladed-style full-field wind input file: '"//TRIM(InputFileData%BladedFF_FileName)//"'", &
      !         ErrStat,ErrMsg,RoutineName)
      !ENDIF

      RETURN
   END SUBROUTINE BladedFF_ValidateInput



   !> This routine checks to see if coherent turbulence can be used or not.  We check the path and file
   !! information before proceeding.  The coherent turbulence info is loaded after the full-field files have
   !! been read, and those can be very slow to load.  So we preemptively check the existence of the coherent
   !! turbulence file.  Further checking that the coherent turbulence can actually be used is check by the
   !! CTTS_Wind submodule (after the loading of the other stuff).
   SUBROUTINE CTTS_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="CTTS_ValidateInput"

         ! Local variables
      LOGICAL                                         :: TmpFileExist

         ! Check if coherent turbulence can be used or not.  It is only applicable to the TSFF and BladedFF wind
         ! file types.
      IF ( InputFileData%CTTS_CoherentTurb .AND. ( .NOT. InputFileData%WindType == TSFF_WindNumber ) &
               .AND. ( .NOT. InputFileData%WindType == BladedFF_WindNumber ) ) THEN
         CALL SetErrStat(ErrID_Fatal,' Coherent turbulence may only be used with full field wind data (WindType = '// &
               TRIM(Num2LStr(TSFF_WindNumber))//' or '//TRIM(Num2LStr(BladedFF_WindNumber))//').', &
               ErrStat,ErrMsg,RoutineName)
         RETURN
      ENDIF

         ! Check that the CT input file exists.
      INQUIRE( file=InputFileData%CTTS_FileName, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find the coherent turbulence input file: '"//TRIM(InputFileData%CTTS_FileName)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

         ! Check that the CT Event path exists.
      ! Unfortunately there is no good portable method to do this, so we won't try.
!FIXME: See if we have anything in the library.  -- Nothing I found...

      RETURN

   END SUBROUTINE CTTS_ValidateInput



   !> This routine checks the HAWC wind file type section of the input file.
   SUBROUTINE HAWC_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="HAWC_ValidateInput"

         ! Local variables
      LOGICAL                                         :: TmpFileExist

         ! Check that the wind files exist.
      INQUIRE( file=InputFileData%HAWC_FileName_u, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find the HAWC u-component wind file: '"//TRIM(InputFileData%HAWC_FileName_u)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF

      INQUIRE( file=InputFileData%HAWC_FileName_v, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find the HAWC v-component wind file: '"//TRIM(InputFileData%HAWC_FileName_v)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF
      INQUIRE( file=InputFileData%HAWC_FileName_w, exist=TmpFileExist )
      IF ( .NOT. TmpFileExist ) THEN
         CALL SetErrStat( ErrID_Fatal," Cannot find the HAWC w-component wind file: '"//TRIM(InputFileData%HAWC_FileName_w)//"'", &
               ErrStat,ErrMsg,RoutineName)
      ENDIF


      ! we'll check the rest of these in the HAWC_Init() routine

      RETURN

   END SUBROUTINE HAWC_ValidateInput



   !> This routine is a placeholder for later development by the end user.  It should only be used if additional inputs
   !! for the user defined routines are added to the input file. Otherwise, this routine may be ignored by the user.
   SUBROUTINE User_ValidateInput()

      CHARACTER(*),              PARAMETER                  :: RoutineName="User_ValidateInput"


      CALL SetErrStat( ErrID_Warn,' This subroutine has not been written yet.',ErrStat,ErrMsg,RoutineName)

      RETURN
   END SUBROUTINE User_ValidateInput


END SUBROUTINE InflowWind_ValidateInput





!====================================================================================================
!> This private subroutine copies the info from the input file over to the parameters for InflowWind.
SUBROUTINE InflowWind_SetParameters( InitInp, InputFileData, p, m, ErrStat, ErrMsg )

      IMPLICIT                                           NONE

      CHARACTER(*),              PARAMETER            :: RoutineName="InflowWind_SetParameters"


      ! Passed variables

   TYPE(InflowWind_InitInputType),     INTENT(IN   )  :: InitInp              !< Input data for initialization
   TYPE(InflowWind_InputFile),         INTENT(IN   )  :: InputFileData        !< The data for initialization
   TYPE(InflowWind_ParameterType),     INTENT(INOUT)  :: p                    !< The parameters for InflowWind
   TYPE(InflowWind_MiscVarType),       INTENT(INOUT)  :: m                    !< The misc/optimization variables for InflowWind
   INTEGER(IntKi),                     INTENT(  OUT)  :: ErrStat              !< Error status  from this subroutine
   CHARACTER(*),                       INTENT(  OUT)  :: ErrMsg               !< Error message from this subroutine


      ! Temporary variables
   INTEGER(IntKi)                                     :: TmpErrStat           !< Temporary error status  for subroutine and function calls
   CHARACTER(ErrMsgLen)                               :: TmpErrMsg            !< Temporary error message for subroutine and function calls
   integer(IntKi)                                     :: NumPtsAvg            !< Number of points to use for disk average vel (1 if no radius)

      ! Local variables
   INTEGER(IntKi)                                     :: I                    !< Generic counter
   REAL(ReKi)                                         :: theta
   REAL(ReKi)                                         :: R ! radius


      ! Initialize ErrStat

   ErrStat = ErrID_None
   ErrMsg  = ""


   !-----------------------------------------------------------------
   ! Copy over the general information that applies to everything
   !-----------------------------------------------------------------

      ! Convert the PropagationDir to radians and store this.  For simplicity, we will shift it to be between -pi and pi
   p%FlowField%PropagationDir = D2R * InputFileData%PropagationDir
   CALL MPi2Pi( p%FlowField%PropagationDir )         ! Shift if necessary so that the value is between -pi and pi

   p%FlowField%VFlowAngle = D2R * InputFileData%VFlowAngle  

      ! Copy over the list of wind coordinates.  Move the arrays to the new one.
   p%NWindVel   =  InputFileData%NWindVel
   CALL AllocAry( p%WindViXYZ, 3, p%NWindVel, "XYZ coordinates of the requested wind points.", TmpErrStat, TmpErrMsg )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF (ErrStat >= AbortErrLev) RETURN

   p%WindViXYZ(1,1:p%NWindVel)  =  InputFileData%WindVxiList(1:p%NWindVel)
   p%WindViXYZ(2,1:p%NWindVel)  =  InputFileData%WindVyiList(1:p%NWindVel)
   p%WindViXYZ(3,1:p%NWindVel)  =  InputFileData%WindVziList(1:p%NWindVel)


      ! Set the number of OutList names read in from the file
   p%NumOuts =  InputFileData%NumOuts


      ! Allocate array used for calculating the AllOuts values.  This has a total of nine 3d velocity vectors
      ! in it (one for each of the possible WindViXYZ points).  This gets passed into CalcOutput at each timestep,
      ! although only the first NWindVel velocity vectors gets calculated.  
   CALL AllocAry( m%WindViUVW, 3, p%NWindVel, "Array of wind velocities corresponding to the WindViLists", TmpErrStat, TmpErrMsg )
   CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName)
   IF ( ErrStat>= AbortErrLev ) RETURN
   m%WindViUVW   =  0.0_ReKi

      ! If output acceleration flag is set, allocate array to hold wind point accelerations.
   IF (InitInp%OutputAccel) THEN
      CALL AllocAry( m%WindAiUVW, 3, p%NWindVel, "Array of wind accelerations corresponding to the WindViLists", TmpErrStat, TmpErrMsg )
      CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName)
      IF ( ErrStat>= AbortErrLev ) RETURN
      m%WindAiUVW   =  0.0_ReKi
   END IF

   CALL AllocAry( m%u_Hub%PositionXYZ, 3, 1, "Array of positions for hub values", TmpErrStat, TmpErrMsg )
      CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName)
   CALL AllocAry( m%y_Hub%VelocityUVW, 3, 1, "Array of velocities for hub values", TmpErrStat, TmpErrMsg )
      CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName)
      
   ! Disk Velocity calculations
   if (InitInp%RadAvg < 0.0_ReKi)  then
      NumPtsAvg = 1_IntKi              ! Use only hub point
   else
      NumPtsAvg = IfW_NumPtsAvg        ! Use a field of points for disk average calculations
   endif

   CALL AllocAry( m%u_Avg%PositionXYZ, 3, NumPtsAvg, "Array of positions for rotor-averaged values", TmpErrStat, TmpErrMsg )
      CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName)
   CALL AllocAry( m%y_Avg%VelocityUVW, 3, NumPtsAvg, "Array of velocities for rotor-averaged values", TmpErrStat, TmpErrMsg )
      CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName)
   CALL AllocAry( p%PositionAvg, 3, NumPtsAvg, "Array of positions for computing average wind speed", TmpErrStat, TmpErrMsg )
      CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName)
      IF ( ErrStat>= AbortErrLev ) RETURN
      
      
   if (InitInp%RadAvg < 0.0_ReKi)  then
      p%PositionAvg = 0.0_ReKi         ! Use only hub
   else
      ! Calculate a ring of points at 70 rotor radius
      R = InitInp%RadAvg * 0.7_ReKi !70% radius
      do i=1,NumPtsAvg
         theta = pi +(i-1)*TwoPi/NumPtsAvg
         p%PositionAvg(1,i) = 0.0_ReKi          ! Hub X (perpindicular to rotor plane)
         p%PositionAvg(2,i) = R*cos(theta)      ! Hub Y
         p%PositionAvg(3,i) = R*sin(theta)      ! Hub Z (in vertical plane when azimuth=0)
      end do
   end if
   
   p%OutputAccel = InitInp%OutputAccel

      ! Set the OutList
   CALL SetOutParam( InputFileData%OutList, p, TmpErrStat, TmpErrmsg )
   CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName)
   IF ( ErrStat>= AbortErrLev ) RETURN

   if (InitInp%Linearize) then
      CALL SetOutParamLin( p, TmpErrStat, TmpErrmsg )
           CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName)
   end if
   
      ! Allocate array for AllOuts
   ALLOCATE( m%AllOuts(0:MaxOutPts), Stat=TmpErrStat )
   IF (TmpErrStat/=0) THEN
      CALL SetErrStat(ErrID_Fatal,"Error allocating memory for m%AllOuts.",ErrStat,ErrMsg,RoutineName)
      RETURN
   END IF
   m%AllOuts = 0.0_ReKi
   


         !-------------------------------------------------------------
         ! Setup the coordinate transforms for rotating the wind field.
         !-------------------------------------------------------------

      !> Perform coordinate transformation using the PropagationDir value read in from the input file (the value stored in parameters
      !! was converted from degrees to radians already).
      !! @note    The PropagationDir is given in Meteorological \f$\Delta\phi\f$, so this is the negative of the \f$\Delta\phi\f$
      !!          for polar coordinates
      !!
      !! An upflow angle rotation is also included in the direction cosine matrix.
      !!
      !! The rotation matrix _RotToWind_, (\f$ M \f$), rotates the spatial points (\f$ [ X~ Y~ Z ] \f$) into the coordinate system 
      !! the wind (\f$ [ X'~ Y'~ Z' ] \f$).  This is a rotation about the point \f$ [ 0~ 0~ H ] \f$ (H = hub reference height), first
      !! about the \f$ Y\f$-axis (in the negative direction), followed by a rotation about \f$Z\f$-axis (also in the negative direction
      !! due to the meteorological convention used).  In the implementation, the rotation is about the coordinate \f$ [ 0~ 0~ H ] \f$
      !! where \f$ H \f$ is the reference height of the turbine (hub height).
      !!
      !! \f$  [X'~ Y'~ Z'] = M \left( [X~ Y~ Z] - [ 0~ 0~ H] \right) + [ 0~ 0~ H ] \f$
      !!
      !! where
      !!
      !! \f{eqnarray*}{   
      !! M & = & R(-\theta_y) R(-\theta_z)
      !!   & = & \begin{bmatrix}    \cos(-\theta_y)    & 0                  & -\sin(-\theta_y)    \\
      !!                            0                 & 1                  & 0                  \\
      !!                            \sin(-\theta_y)    & 0                  & \cos(-\theta_y)     \end{bmatrix}
      !!         \begin{bmatrix}    \cos(-\theta_z)    & \sin(-\theta_z)     & 0                  \\
      !!                            -\sin(-\theta_z)   & \cos(-\theta_z)     & 0                  \\
      !!                            0                 &  0                 & 1                  \end{bmatrix}
      !!   & = & \begin{bmatrix}    C_y               & 0                  & -S_y               \\
      !!                            0                 & 1                  & 0                  \\
      !!                            S_y               & 0                  & C_y                \end{bmatrix}
      !!         \begin{bmatrix}    C_z               & S_z                & 0                  \\
      !!                            -S_z              & C_z                & 0                  \\
      !!                            0                 & 0                  & 1                  \end{bmatrix} \\
      !!   & &
      !!   & = & \begin{bmatrix}  
      !!            \cos(-\theta_y) \cos(-\theta_z)     &  \cos(-\theta_y) \sin(-\theta_z)    &   - \sin(-\theta_y)    \\
      !!          - \sin(-\theta_z)                    &  \cos(-\theta_z)                   &     0                 \\
      !!            \sin(-\theta_y) \cos(-\theta_z)     &  \sin(-\theta_y) \sin(-\theta_z)    &     \cos(-\theta_y)    \\
      !!         \end{bmatrix}   
      !!   & = & \begin{bmatrix}  
      !!             C_y C_z     &  C_y S_z     &   - S_y      \\
      !!               - S_z     &      C_z     &     0        \\
      !!             S_y C_z     &  S_y S_z     &     C_y      \\
      !!         \end{bmatrix}   \\
      !! \f}
      !!
      !! where \f$ \theta_z \f$ is the rotation about the Z direction (_PropagationDir_) and \f$ \theta_y \f$ is
      !! the upflow angle (_VFlowAngle_).


      ! Create the rotation matrices -- rotate from XYZ to X'Y'Z' (wind aligned along X) coordinates
      ! Included in this rotation is the wind upflow (inclination) angle (rotation about Y axis)
   p%FlowField%RotToWind(1,:) = [ COS(-p%FlowField%VFlowAngle) * COS(-p%FlowField%PropagationDir),  &
                                  COS(-p%FlowField%VFlowAngle) * SIN(-p%FlowField%PropagationDir),  &
                                  -SIN(-p%FlowField%VFlowAngle) ]
   p%FlowField%RotToWind(2,:) = [ -SIN(-p%FlowField%PropagationDir), &
                                  COS(-p%FlowField%PropagationDir), &
                                  0.0_ReKi ]
   p%FlowField%RotToWind(3,:) = [ SIN(-p%FlowField%VFlowAngle) * COS(-p%FlowField%PropagationDir),  &
                                  SIN(-p%FlowField%VFlowAngle) * SIN(-p%FlowField%PropagationDir),  &
                                  COS(-p%FlowField%VFlowAngle) ]

      ! Create the rotation matrices -- rotate from X'Y'Z' (wind aligned along X) to global XYZ coordinates: this is the same as a
      ! rotation about the (positive) upflow angle multiplied by a rotation about the (positive) wind direction:
      ! Global wind = R(p%PropagationDir) * R(p%VFlowAngle) * [local wind]
      ! loal wind = R( -p%VFlowAngle) * R (-p%PropagationDir) [global wind]
      !           = R^T(p%VFlowAngle) * R^T(p%PropagationDir) [global wind]
      !           = (R(p%PropagationDir) * R(p%VFlowAngle))^T [global wind]
   p%FlowField%RotFromWind =  TRANSPOSE(p%FlowField%RotToWind)

      ! Create the array used for holding the rotated list of WindViXYZ coordinates in the wind reference frame, and populate it
   CALL AllocAry( p%WindViXYZprime, 3, p%NWindVel, 'Array for WindViXYZ coordinates in the wind reference frame', &
               TmpErrStat, TmpErrMsg )
   CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName)
   IF ( ErrStat>= AbortErrLev ) RETURN 
 
   p%WindViXYZprime   =  0.0_ReKi
      ! set the output points. Note rotation is about the hub height at [0 0 H].  See InflowWind_SetParameters for details.
   DO I = 1,p%NWindVel
      p%WindViXYZprime(:,I) =  MATMUL( p%FlowField%RotToWind, (p%WindViXYZ(:,I) - p%FlowField%RefPosition )) + p%FlowField%RefPosition
   ENDDO
   
   p%FlowField%RotateWindBox = .not. (EqualRealNos (p%FlowField%PropagationDir, 0.0_ReKi) .AND. EqualRealNos (p%FlowField%VFlowAngle, 0.0_ReKi))
   
END SUBROUTINE InflowWind_SetParameters







!**********************************************************************************************************************************
! NOTE: The following lines of code were generated by a Matlab script called "Write_ChckOutLst.m"
!      using the parameters listed in the "OutListParameters.xlsx" Excel file. Any changes to these 
!      lines should be modified in the Matlab script and/or Excel worksheet as necessary. 
!----------------------------------------------------------------------------------------------------------------------------------
!> This routine checks to see if any requested output channel names (stored in the OutList(:)) are invalid. It returns a 
!! warning if any of the channels are not available outputs from the module.
!!  It assigns the settings for OutParam(:) (i.e, the index, name, and units of the output channels, WriteOutput(:)).
!!  the sign is set to 0 if the channel is invalid.
!! It sets assumes the value p%NumOuts has been set before this routine has been called, and it sets the values of p%OutParam here.
!! 
!! This routine was generated by Write_ChckOutLst.m using the parameters listed in OutListParameters.xlsx at 29-Jul-2022 19:48:39.
SUBROUTINE SetOutParam(OutList, p, ErrStat, ErrMsg )
!..................................................................................................................................

  IMPLICIT                        NONE

      ! Passed variables

   CHARACTER(ChanLen),        INTENT(IN)     :: OutList(:)                        !< The list of user-requested outputs
   TYPE(InflowWind_ParameterType), INTENT(INOUT)  :: p                         !< The module parameters
   INTEGER(IntKi),            INTENT(OUT)    :: ErrStat                           !< The error status code
   CHARACTER(*),              INTENT(OUT)    :: ErrMsg                            !< The error message, if an error occurred

      ! Local variables

   INTEGER                      :: ErrStat2                                        ! temporary (local) error status
   INTEGER                      :: I                                               ! Generic loop-counting index
   INTEGER                      :: INDX                                            ! Index for valid arrays

   LOGICAL                      :: CheckOutListAgain                               ! Flag used to determine if output parameter starting with "M" is valid (or the negative of another parameter)
   LOGICAL                      :: InvalidOutput(0:MaxOutPts)                      ! This array determines if the output channel is valid for this configuration
   CHARACTER(ChanLen)           :: OutListTmp                                      ! A string to temporarily hold OutList(I)
   CHARACTER(*), PARAMETER      :: RoutineName = "SetOutParam"

   CHARACTER(OutStrLenM1), PARAMETER  :: ValidParamAry(98) =  (/  &   ! This lists the names of the allowed parameters, which must be sorted alphabetically
                               "WIND1ACCX     ","WIND1ACCY     ","WIND1ACCZ     ","WIND1ANGXY    ","WIND1VELMAG   ", &
                               "WIND1VELX     ","WIND1VELXY    ","WIND1VELY     ","WIND1VELZ     ","WIND2ACCX     ", &
                               "WIND2ACCY     ","WIND2ACCZ     ","WIND2ANGXY    ","WIND2VELMAG   ","WIND2VELX     ", &
                               "WIND2VELXY    ","WIND2VELY     ","WIND2VELZ     ","WIND3ACCX     ","WIND3ACCY     ", &
                               "WIND3ACCZ     ","WIND3ANGXY    ","WIND3VELMAG   ","WIND3VELX     ","WIND3VELXY    ", &
                               "WIND3VELY     ","WIND3VELZ     ","WIND4ACCX     ","WIND4ACCY     ","WIND4ACCZ     ", &
                               "WIND4ANGXY    ","WIND4VELMAG   ","WIND4VELX     ","WIND4VELXY    ","WIND4VELY     ", &
                               "WIND4VELZ     ","WIND5ACCX     ","WIND5ACCY     ","WIND5ACCZ     ","WIND5ANGXY    ", &
                               "WIND5VELMAG   ","WIND5VELX     ","WIND5VELXY    ","WIND5VELY     ","WIND5VELZ     ", &
                               "WIND6ACCX     ","WIND6ACCY     ","WIND6ACCZ     ","WIND6ANGXY    ","WIND6VELMAG   ", &
                               "WIND6VELX     ","WIND6VELXY    ","WIND6VELY     ","WIND6VELZ     ","WIND7ACCX     ", &
                               "WIND7ACCY     ","WIND7ACCZ     ","WIND7ANGXY    ","WIND7VELMAG   ","WIND7VELX     ", &
                               "WIND7VELXY    ","WIND7VELY     ","WIND7VELZ     ","WIND8ACCX     ","WIND8ACCY     ", &
                               "WIND8ACCZ     ","WIND8ANGXY    ","WIND8VELMAG   ","WIND8VELX     ","WIND8VELXY    ", &
                               "WIND8VELY     ","WIND8VELZ     ","WIND9ACCX     ","WIND9ACCY     ","WIND9ACCZ     ", &
                               "WIND9ANGXY    ","WIND9VELMAG   ","WIND9VELX     ","WIND9VELXY    ","WIND9VELY     ", &
                               "WIND9VELZ     ","WINDDISKANGXY ","WINDDISKVELMAG","WINDDISKVELX  ","WINDDISKVELXY ", &
                               "WINDDISKVELY  ","WINDDISKVELZ  ","WINDHUBANGXY  ","WINDHUBVELMAG ","WINDHUBVELX   ", &
                               "WINDHUBVELXY  ","WINDHUBVELY   ","WINDHUBVELZ   ","WINDMEAS1     ","WINDMEAS2     ", &
                               "WINDMEAS3     ","WINDMEAS4     ","WINDMEAS5     "/)

   INTEGER(IntKi), PARAMETER :: ParamIndxAry(98) =  (/ &                            ! This lists the index into AllOuts(:) of the allowed parameters ValidParamAry(:)
                                     Wind1AccX ,      Wind1AccY ,      Wind1AccZ ,     Wind1AngXY ,    Wind1VelMag , &
                                     Wind1VelX ,     Wind1VelXY ,      Wind1VelY ,      Wind1VelZ ,      Wind2AccX , &
                                     Wind2AccY ,      Wind2AccZ ,     Wind2AngXY ,    Wind2VelMag ,      Wind2VelX , &
                                    Wind2VelXY ,      Wind2VelY ,      Wind2VelZ ,      Wind3AccX ,      Wind3AccY , &
                                     Wind3AccZ ,     Wind3AngXY ,    Wind3VelMag ,      Wind3VelX ,     Wind3VelXY , &
                                     Wind3VelY ,      Wind3VelZ ,      Wind4AccX ,      Wind4AccY ,      Wind4AccZ , &
                                    Wind4AngXY ,    Wind4VelMag ,      Wind4VelX ,     Wind4VelXY ,      Wind4VelY , &
                                     Wind4VelZ ,      Wind5AccX ,      Wind5AccY ,      Wind5AccZ ,     Wind5AngXY , &
                                   Wind5VelMag ,      Wind5VelX ,     Wind5VelXY ,      Wind5VelY ,      Wind5VelZ , &
                                     Wind6AccX ,      Wind6AccY ,      Wind6AccZ ,     Wind6AngXY ,    Wind6VelMag , &
                                     Wind6VelX ,     Wind6VelXY ,      Wind6VelY ,      Wind6VelZ ,      Wind7AccX , &
                                     Wind7AccY ,      Wind7AccZ ,     Wind7AngXY ,    Wind7VelMag ,      Wind7VelX , &
                                    Wind7VelXY ,      Wind7VelY ,      Wind7VelZ ,      Wind8AccX ,      Wind8AccY , &
                                     Wind8AccZ ,     Wind8AngXY ,    Wind8VelMag ,      Wind8VelX ,     Wind8VelXY , &
                                     Wind8VelY ,      Wind8VelZ ,      Wind9AccX ,      Wind9AccY ,      Wind9AccZ , &
                                    Wind9AngXY ,    Wind9VelMag ,      Wind9VelX ,     Wind9VelXY ,      Wind9VelY , &
                                     Wind9VelZ ,  WindDiskAngXY , WindDiskVelMag ,   WindDiskVelX ,  WindDiskVelXY , &
                                  WindDiskVelY ,   WindDiskVelZ ,   WindHubAngXY ,  WindHubVelMag ,    WindHubVelX , &
                                  WindHubVelXY ,    WindHubVelY ,    WindHubVelZ ,      WindMeas1 ,      WindMeas2 , &
                                     WindMeas3 ,      WindMeas4 ,      WindMeas5 /)

   CHARACTER(ChanLen), PARAMETER :: ParamUnitsAry(98) =  (/ character(ChanLen) :: &  ! This lists the units corresponding to the allowed parameters
                               "(m/s)","(m/s)","(m/s)","(deg)","(m/s)", &
                               "(m/s)","(m/s)","(m/s)","(m/s)","(m/s)", &
                               "(m/s)","(m/s)","(deg)","(m/s)","(m/s)", &
                               "(m/s)","(m/s)","(m/s)","(m/s)","(m/s)", &
                               "(m/s)","(deg)","(m/s)","(m/s)","(m/s)", &
                               "(m/s)","(m/s)","(m/s)","(m/s)","(m/s)", &
                               "(deg)","(m/s)","(m/s)","(m/s)","(m/s)", &
                               "(m/s)","(m/s)","(m/s)","(m/s)","(deg)", &
                               "(m/s)","(m/s)","(m/s)","(m/s)","(m/s)", &
                               "(m/s)","(m/s)","(m/s)","(deg)","(m/s)", &
                               "(m/s)","(m/s)","(m/s)","(m/s)","(m/s)", &
                               "(m/s)","(m/s)","(deg)","(m/s)","(m/s)", &
                               "(m/s)","(m/s)","(m/s)","(m/s)","(m/s)", &
                               "(m/s)","(deg)","(m/s)","(m/s)","(m/s)", &
                               "(m/s)","(m/s)","(m/s)","(m/s)","(m/s)", &
                               "(deg)","(m/s)","(m/s)","(m/s)","(m/s)", &
                               "(m/s)","(deg)","(m/s)","(m/s)","(m/s)", &
                               "(m/s)","(m/s)","(deg)","(m/s)","(m/s)", &
                               "(m/s)","(m/s)","(m/s)","(m/s)","(m/s)", &
                               "(m/s)","(m/s)","(m/s)"/)

      ! Initialize values
   ErrStat = ErrID_None
   ErrMsg = ""
   InvalidOutput = .FALSE.


!   ..... Developer must add checking for invalid inputs here: .....
      ! NOTE:  we are not checking that the coordinates input for the WindVxi, WindVyi, and WindVzi are valid here.  We are
      !        checking that at the input file validation (they simply get zeroed with a warning if there is an issue).

      ! make sure we don't ask for outputs that don't exist:
   DO I = p%NWindVel+1, 9      
      InvalidOutput( WindVelX(  I) ) =  .TRUE.
      InvalidOutput( WindVelY(  I) ) =  .TRUE.
      InvalidOutput( WindVelZ(  I) ) =  .TRUE.
      InvalidOutput( WindAccX(  I) ) =  .TRUE.
      InvalidOutput( WindAccY(  I) ) =  .TRUE.
      InvalidOutput( WindAccZ(  I) ) =  .TRUE.
      InvalidOutput( WindVelXY( I) ) =  .TRUE.
      InvalidOutput( WindVelMag(I) ) =  .TRUE.
      InvalidOutput( WindAngXY( I) ) =  .TRUE.
   END DO   

      ! Set all accelerations invalid if not requested
   if (.not. p%OutputAccel) then
      InvalidOutput(WindAccX) = .TRUE.
      InvalidOutput(WindAccY) = .TRUE.
      InvalidOutput(WindAccZ) = .TRUE.
   end if

   do I=p%lidar%NumMeasurements+1,SIZE(WindMeas)
      InvalidOutput( WindMeas(I) ) = .TRUE.
   end do

!   ................. End of validity checking .................


   !-------------------------------------------------------------------------------------------------
   ! Allocate and set index, name, and units for the output channels
   ! If a selected output channel is not available in this module, set error flag.
   !-------------------------------------------------------------------------------------------------

   ALLOCATE ( p%OutParam(0:p%NumOuts) , STAT=ErrStat2 )
   IF ( ErrStat2 /= 0_IntKi )  THEN
      CALL SetErrStat( ErrID_Fatal,"Error allocating memory for the InflowWind OutParam array.", ErrStat, ErrMsg, RoutineName )
      RETURN
   ENDIF

      ! Set index, name, and units for the time output channel:

   p%OutParam(0)%Indx  = Time
   p%OutParam(0)%Name  = "Time"    ! OutParam(0) is the time channel by default.
   p%OutParam(0)%Units = "(s)"
   p%OutParam(0)%SignM = 1


      ! Set index, name, and units for all of the output channels.
      ! If a selected output channel is not available by this module set ErrStat = ErrID_Warn.

   DO I = 1,p%NumOuts

      p%OutParam(I)%Name  = OutList(I)
      OutListTmp          = OutList(I)

      ! Reverse the sign (+/-) of the output channel if the user prefixed the
      !   channel name with a "-", "_", "m", or "M" character indicating "minus".


      CheckOutListAgain = .FALSE.

      IF      ( INDEX( "-_", OutListTmp(1:1) ) > 0 ) THEN
         p%OutParam(I)%SignM = -1                         ! ex, "-TipDxc1" causes the sign of TipDxc1 to be switched.
         OutListTmp          = OutListTmp(2:)
      ELSE IF ( INDEX( "mM", OutListTmp(1:1) ) > 0 ) THEN ! We'll assume this is a variable name for now, (if not, we will check later if OutListTmp(2:) is also a variable name)
         CheckOutListAgain   = .TRUE.
         p%OutParam(I)%SignM = 1
      ELSE
         p%OutParam(I)%SignM = 1
      END IF

      CALL Conv2UC( OutListTmp )    ! Convert OutListTmp to upper case


      Indx = IndexCharAry( OutListTmp(1:OutStrLenM1), ValidParamAry )


         ! If it started with an "M" (CheckOutListAgain) we didn't find the value in our list (Indx < 1)

      IF ( CheckOutListAgain .AND. Indx < 1 ) THEN    ! Let's assume that "M" really meant "minus" and then test again
         p%OutParam(I)%SignM = -1                     ! ex, "MTipDxc1" causes the sign of TipDxc1 to be switched.
         OutListTmp          = OutListTmp(2:)

         Indx = IndexCharAry( OutListTmp(1:OutStrLenM1), ValidParamAry )
      END IF


      IF ( Indx > 0 ) THEN ! we found the channel name
         IF ( InvalidOutput( ParamIndxAry(Indx) ) ) THEN  ! but, it isn't valid for these settings
            p%OutParam(I)%Indx  = 0                 ! pick any valid channel (I just picked "Time=0" here because it's universal)
            p%OutParam(I)%Units = "INVALID"
            p%OutParam(I)%SignM = 0
         ELSE
            p%OutParam(I)%Indx  = ParamIndxAry(Indx)
            p%OutParam(I)%Units = ParamUnitsAry(Indx) ! it's a valid output
         END IF
      ELSE ! this channel isn't valid
         p%OutParam(I)%Indx  = 0                    ! pick any valid channel (I just picked "Time=0" here because it's universal)
         p%OutParam(I)%Units = "INVALID"
         p%OutParam(I)%SignM = 0                    ! multiply all results by zero

         CALL SetErrStat(ErrID_Fatal, TRIM(p%OutParam(I)%Name)//" is not an available output channel.",ErrStat,ErrMsg,RoutineName)
      END IF

   END DO

   RETURN
END SUBROUTINE SetOutParam
!----------------------------------------------------------------------------------------------------------------------------------
!End of code generated by Matlab script
!**********************************************************************************************************************************

!..................................................................................................................................
!> This routine checks to see if any requested output channel names are to be output in linearization analysis.
!! note that we output all WriteOutput values and assume that none of them depend on inputs (so I don't need this mapping any more)
SUBROUTINE SetOutParamLin( p, ErrStat, ErrMsg )

   IMPLICIT                        NONE

      ! Passed variables

   TYPE(InflowWind_ParameterType),    INTENT(INOUT)  :: p                  !< The module parameters
   INTEGER(IntKi),                    INTENT(OUT)    :: ErrStat            !< The error status code
   CHARACTER(*),                      INTENT(OUT)    :: ErrMsg             !< The error message, if an error occurred

      ! Local variables

   INTEGER                      :: ErrStat2                                        ! temporary (local) error status
   INTEGER                      :: I                                               ! Generic loop-counting index
   INTEGER                      :: J                                               ! Generic loop-counting index
   CHARACTER(ErrMsgLen)         :: ErrMsg2
   CHARACTER(*), PARAMETER      :: RoutineName = "SetOutParamLin"

   ErrStat = ErrID_None
   ErrMsg  = ""
   
   call AllocAry(p%OutParamLinIndx, 2, p%NumOuts, 'OutParamLinIndx', ErrStat2, ErrMsg2)
   call setErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName)
   if (ErrStat >= AbortErrLev) return
      
   p%OutParamLinIndx = 0 ! initialize to 0
   do i = 1,p%NumOuts
      if (p%OutParam(i)%SignM /= 0 ) then
         
         do j=1,size(WindVelX)
            if ( p%OutParam(i)%Indx == WindVelX(j) ) then
               p%OutParamLinIndx(1,i) = j
               p%OutParamLinIndx(2,i) = 1
               exit !exit j loop; move to next parameter
            elseif ( p%OutParam(i)%Indx == WindVelY(j) ) then
               p%OutParamLinIndx(1,i) = j
               p%OutParamLinIndx(2,i) = 2
               exit !exit j loop; move to next parameter
            elseif ( p%OutParam(i)%Indx == WindVelZ(j) ) then
               p%OutParamLinIndx(1,i) = j
               p%OutParamLinIndx(2,i) = 3
               exit !exit j loop; move to next parameter
            else !bjj: initialize this in case we have the new outputs that aren't just X-Y-Z velocities. CHECK THIS!!!
               p%OutParamLinIndx(1,i) = 0
               p%OutParamLinIndx(2,i) = 0
            end if
         end do
         
      end if
   end do
   
                                
   
END SUBROUTINE SetOutParamLin
!..................................................................................................................................
SUBROUTINE SetAllOuts( p, y, m, ErrStat, ErrMsg )

   IMPLICIT                                              NONE

   CHARACTER(*),              PARAMETER               :: RoutineName="SetAllOuts"


   TYPE(InflowWind_ParameterType),     INTENT(IN   )  :: p            !< The parameters for InflowWind
   TYPE(InflowWind_OutputType),        INTENT(IN   )  :: y            !< Outputs
   TYPE(InflowWind_MiscVarType),       INTENT(INOUT)  :: m            !< Misc variables for optimization (not copied in glue code)
   INTEGER(IntKi),                     INTENT(  OUT)  :: ErrStat      !< Error status  from this subroutine
   CHARACTER(*),                       INTENT(  OUT)  :: ErrMsg       !< Error message from this subroutine


      ! Local Variables
   INTEGER(IntKi)                                     :: I            ! Generic counter


      ! Initialization
   ErrStat  = ErrID_None
   ErrMsg   = ''
   
   if (p%NumOuts < 1) return

      ! We set the unused values to 0 at init, so we don't need to set them again here:
   DO I = 1,p%NWindVel
      
      m%AllOuts( WindVelX(I) ) =  m%WindViUVW(1,I)
      m%AllOuts( WindVelY(I) ) =  m%WindViUVW(2,I)
      m%AllOuts( WindVelZ(I) ) =  m%WindViUVW(3,I)

      m%AllOuts( WindVelXY(I) ) =  SQRT( m%WindViUVW(1,I)**2 + m%WindViUVW(2,I)**2 )
      m%AllOuts( WindVelMag(I) ) =  SQRT( m%WindViUVW(1,I)**2 + m%WindViUVW(2,I)**2 + m%WindViUVW(3,I)**2 )
      if ( m%AllOuts( WindVelXY(I) ) == 0.0_ReKi) then
         m%AllOuts( WindAngXY(I) ) = 0.0_ReKi
      else
         m%AllOuts( WindAngXY(I) ) =  ATAN2( m%WindViUVW(2,I), m%WindViUVW(1,I) )*R2D ! in degrees
      end if
      
   END DO

      ! Set accelerations 
   if (allocated(m%WindAiUVW)) then
      DO I = 1,p%NWindVel
         m%AllOuts( WindAccX(I) ) =  m%WindAiUVW(1,I)
         m%AllOuts( WindAccY(I) ) =  m%WindAiUVW(2,I)
         m%AllOuts( WindAccZ(I) ) =  m%WindAiUVW(3,I)
      END DO
   end if
   
      ! DiskVel outputs:
   m%AllOuts( WindDiskVelX   ) =  y%DiskVel(1)
   m%AllOuts( WindDiskVelY   ) =  y%DiskVel(2)
   m%AllOuts( WindDiskVelZ   ) =  y%DiskVel(3)
   m%AllOuts( WindDiskVelXY  ) =  SQRT( y%DiskVel(1)**2 + y%DiskVel(2)**2 )
   m%AllOuts( WindDiskVelMag ) =  TwoNorm(y%DiskVel)
   if ( m%AllOuts( WindDiskVelXY ) == 0.0_ReKi) then
      m%AllOuts( WindDiskAngXY ) = 0.0_ReKi
   else
      m%AllOuts( WindDiskAngXY ) =  ATAN2( y%DiskVel(2), y%DiskVel(1) )*R2D ! in degrees
   end if
   
      ! DiskVel outputs:
   m%AllOuts( WindHubVelX   ) =  y%HubVel(1)
   m%AllOuts( WindHubVelY   ) =  y%HubVel(2)
   m%AllOuts( WindHubVelZ   ) =  y%HubVel(3)
   m%AllOuts( WindHubVelXY  ) =  SQRT( y%HubVel(1)**2 + y%HubVel(2)**2 )
   m%AllOuts( WindHubVelMag ) =  TwoNorm(y%HubVel)
   if ( m%AllOuts( WindHubVelXY ) == 0.0_ReKi) then
      m%AllOuts( WindHubAngXY ) = 0.0_ReKi
   else
      m%AllOuts( WindHubAngXY ) =  ATAN2( y%HubVel(2), y%HubVel(1) )*R2D ! in degrees
   end if
   
   
      !FIXME:  Add in Wind1Dir etc.  -- although those can be derived outside of FAST.
   if (ALLOCATED(y%lidar%LidSpeed)) then
      DO I = 1,MIN(SIZE(WindMeas), SIZE(y%lidar%LidSpeed) )
         m%AllOuts( WindMeas(I) ) = y%lidar%LidSpeed(I)
      END DO
   endif

END SUBROUTINE SetAllOuts




!====================================================================================================
SUBROUTINE InflowWind_OpenSumFile( SumFileUnit, SummaryName, IfW_Prog, WindType, ErrStat, ErrMsg )


      ! Passed variables

   INTEGER(IntKi),                  INTENT(  OUT)  :: SumFileUnit    !< the unit number for the InflowWindsummary file
   CHARACTER(*),                    INTENT(IN   )  :: SummaryName    !< the name of the InflowWind summary file
   TYPE(ProgDesc),                  INTENT(IN   )  :: IfW_Prog       !< the name/version/date of the InflowWind program
   INTEGER(IntKi),                  INTENT(IN   )  :: WindType       !< type identifying what wind we are using
   INTEGER(IntKi),                  INTENT(  OUT)  :: ErrStat        !< returns a non-zero value when an error occurs
   CHARACTER(*),                    INTENT(  OUT)  :: ErrMsg         !< Error message if ErrStat /= ErrID_None


   INTEGER(IntKi)                                  :: TmpErrStat     !< Temporary error status for checking how the WRITE worked

       ! Initialize ErrStat

   ErrStat = ErrID_None
   ErrMsg  = ""

   SumFileUnit = -1
   !$OMP critical(fileopen_critical)
   CALL GetNewUnit( SumFileUnit )
   CALL OpenFOutFile ( SumFileUnit, SummaryName, ErrStat, ErrMsg )
   !$OMP end critical(fileopen_critical)
   IF (ErrStat >=AbortErrLev) RETURN


         ! Write the summary file header
   WRITE(SumFileUnit,'(/,A/)',IOSTAT=TmpErrStat)   'This summary file was generated by '//TRIM( IfW_Prog%Name )//&
                     ' '//TRIM( IfW_Prog%Ver )//' on '//CurDate()//' at '//CurTime()//'.'
   WRITE(SumFileUnit,'(A14,I1)',IOSTAT=TmpErrStat) '  WindType:   ',WindType
   IF ( TmpErrStat /= 0 ) THEN
      CALL SetErrStat(ErrID_Fatal,'Error writing to summary file.',ErrStat,ErrMsg,'')
      RETURN
   END IF

END SUBROUTINE InflowWind_OpenSumFile

!====================================================================================================
SUBROUTINE InflowWind_CloseSumFile( SumFileUnit, ErrStat, ErrMsg )


      ! Passed variables

   INTEGER(IntKi),                  INTENT(INOUT)  :: SumFileUnit    !< the unit number for the InflowWindsummary file
   INTEGER(IntKi),                  INTENT(  OUT)  :: ErrStat        !< returns a non-zero value when an error occurs
   CHARACTER(*),                    INTENT(  OUT)  :: ErrMsg         !< Error message if ErrStat /= ErrID_None


      ! Local variables
   INTEGER(IntKi)                                  :: TmpErrStat

      ! Initialize ErrStat

   ErrStat     = ErrID_None
   ErrMsg      = ''


      ! Write any closing information in the summary file
   IF ( SumFileUnit > 0_IntKi ) THEN
      WRITE (SumFileUnit,'(/,A/)', IOSTAT=TmpErrStat)  'This summary file was closed on '//CurDate()//' at '//CurTime()//'.'
      IF (TmpErrStat /= 0_IntKi)    CALL SetErrStat( ErrID_Fatal, 'Problem writing to the InflowWind summary file.', ErrStat, ErrMsg, '' )

         ! Close the file
      CLOSE( SumFileUnit, IOSTAT=TmpErrStat )
      IF (TmpErrStat /= 0_IntKi)    CALL SetErrStat( ErrID_Fatal, 'Problem closing the InflowWind summary file.', ErrStat, ErrMsg, '' )
   END IF



END SUBROUTINE InflowWind_CloseSumFile

!====================================================================================================



SUBROUTINE CalculateOutput( Time, InputData, p, x, xd, z, OtherStates, y, m, FillWrOut, ErrStat, ErrMsg )

   REAL(DbKi),                               INTENT(IN   )  :: Time           !< Current simulation time in seconds
   TYPE(InflowWind_InputType),               INTENT(IN   )  :: InputData      !< Inputs at Time
   TYPE(InflowWind_ParameterType),           INTENT(IN   )  :: p              !< Parameters
   TYPE(InflowWind_ContinuousStateType),     INTENT(IN   )  :: x              !< Continuous states at Time
   TYPE(InflowWind_DiscreteStateType),       INTENT(IN   )  :: xd             !< Discrete states at Time
   TYPE(InflowWind_ConstraintStateType),     INTENT(IN   )  :: z              !< Constraint states at Time
   TYPE(InflowWind_OtherStateType),          INTENT(IN   )  :: OtherStates    !< Other states at Time
   TYPE(InflowWind_OutputType),              INTENT(INOUT)  :: y              !< Outputs computed at Time (IN for mesh reasons and data allocation)
   TYPE(InflowWind_MiscVarType),             INTENT(INOUT)  :: m              !< misc/optimization variables
   LOGICAL,                                  INTENT(IN   )  :: FillWrOut      !< Flag to determine if we need to fill WriteOutput values
   INTEGER(IntKi),                           INTENT(  OUT)  :: ErrStat        !< Error status of the operation
   CHARACTER(*),                             INTENT(  OUT)  :: ErrMsg         !< Error message if ErrStat /= ErrID_None

   CHARACTER(*),              PARAMETER                     :: RoutineName="CalculateOutput"
   INTEGER(IntKi)                                           :: TmpErrStat
   CHARACTER(ErrMsgLen)                                     :: TmpErrMsg            ! temporary error message

   ErrStat  = ErrID_None
   ErrMsg   = ""

   ! Get velocities and accelerations for the given positions
   CALL IfW_FlowField_GetVelAcc(p%FlowField, 0, Time, InputData%PositionXYZ, &
                                 y%VelocityUVW, y%AccelUVW, TmpErrStat, TmpErrMsg)
   CALL SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName )

   ! Get velocities and accelerations for OutList variables, no error check
   IF ( p%NWindVel >= 1_IntKi .AND. FillWrOut ) THEN
      CALL IfW_FlowField_GetVelAcc(p%FlowField, 0, Time, p%WindViXYZ, &
                                    m%WindViUVW, m%WindAiUVW, TmpErrStat, TmpErrMsg)
   ENDIF

END SUBROUTINE CalculateOutput
                      
!====================================================================================================
!FIXME: is this routine necessary anymore?
!> this routine calculates a rotor-averaged mean velocity, DiskVel 
SUBROUTINE InflowWind_GetRotorSpatialAverage( Time, InputData, p, x, xd, z, OtherStates, m, MeanVelocity, ErrStat, ErrMsg )

   REAL(DbKi),                               INTENT(IN   )  :: Time              !< Current simulation time in seconds
   TYPE(InflowWind_InputType),               INTENT(IN   )  :: InputData         !< Inputs at Time
   TYPE(InflowWind_ParameterType),           INTENT(IN   )  :: p                 !< Parameters
   TYPE(InflowWind_ContinuousStateType),     INTENT(IN   )  :: x                 !< Continuous states at Time
   TYPE(InflowWind_DiscreteStateType),       INTENT(IN   )  :: xd                !< Discrete states at Time
   TYPE(InflowWind_ConstraintStateType),     INTENT(IN   )  :: z                 !< Constraint states at Time
   TYPE(InflowWind_OtherStateType),          INTENT(IN   )  :: OtherStates       !< Other states at Time
   TYPE(InflowWind_MiscVarType),             INTENT(INOUT)  :: m                 !< misc/optimization variables
   REAL(ReKi),                               INTENT(  OUT)  :: MeanVelocity(3)   !< at InputPosition      
   INTEGER(IntKi),                           INTENT(  OUT)  :: ErrStat           !< Error status of the operation
   CHARACTER(*),                             INTENT(  OUT)  :: ErrMsg            !< Error message if ErrStat /= ErrID_None
   
   CHARACTER(*),PARAMETER     :: RoutineName = "InflowWind_GetRotorSpatialAverage"
   INTEGER(IntKi)             :: ErrStat2
   CHARACTER(ErrMsgLen)       :: ErrMsg2   
   INTEGER(IntKi)             :: I
   REAL(ReKi), ALLOCATABLE    :: acc(:,:)  ! acceleration, unallocated so it won't be calculated

   ErrStat  = ErrID_None
   ErrMsg   = ""
   MeanVelocity = 0.0_ReKi
   
   !----------------------------------------------------------------------------
   ! Points coordinate transforms from to global to aligned with hub orientation
   !----------------------------------------------------------------------------

   m%u_Avg%HubPosition    = InputData%HubPosition
   m%u_Avg%HubOrientation = InputData%HubOrientation
   
   do i=1,size(m%u_Avg%PositionXYZ,DIM=2)
      m%u_Avg%PositionXYZ(:,i) = InputData%HubPosition + &
                                 matmul(InputData%HubOrientation,p%PositionAvg(:,i))
   end do

   !----------------------------------------------------------------------------
   ! Calculate wind velocities at average positions
   !----------------------------------------------------------------------------

   CALL IfW_FlowField_GetVelAcc(p%FlowField, 0, Time, m%u_Avg%PositionXYZ, &
                                m%y_Avg%VelocityUVW, acc, ErrStat2, ErrMsg2)
   CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName )

   !----------------------------------------------------------------------------
   ! Calculate average velocity over all positions
   !----------------------------------------------------------------------------

   MeanVelocity = sum(m%y_Avg%VelocityUVW, dim=2) / REAL(IfW_NumPtsAvg,ReKi)

END SUBROUTINE InflowWind_GetRotorSpatialAverage
!====================================================================================================
!> this routine calculates a rotor-averaged mean velocity, DiskVel 
SUBROUTINE InflowWind_GetHubValues( Time, InputData, p, x, xd, z, OtherStates, m, Velocity, ErrStat, ErrMsg )
   REAL(DbKi),                               INTENT(IN   )  :: Time              !< Current simulation time in seconds
   TYPE(InflowWind_InputType),               INTENT(IN   )  :: InputData         !< Inputs at Time
   TYPE(InflowWind_ParameterType),           INTENT(IN   )  :: p                 !< Parameters
   TYPE(InflowWind_ContinuousStateType),     INTENT(IN   )  :: x                 !< Continuous states at Time
   TYPE(InflowWind_DiscreteStateType),       INTENT(IN   )  :: xd                !< Discrete states at Time
   TYPE(InflowWind_ConstraintStateType),     INTENT(IN   )  :: z                 !< Constraint states at Time
   TYPE(InflowWind_OtherStateType),          INTENT(IN   )  :: OtherStates       !< Other states at Time
   TYPE(InflowWind_MiscVarType),             INTENT(INOUT)  :: m                 !< misc/optimization variables
   REAL(ReKi),                               INTENT(  OUT)  :: Velocity(3)       !< at InputPosition
   INTEGER(IntKi),                           INTENT(  OUT)  :: ErrStat           !< Error status of the operation
   CHARACTER(*),                             INTENT(  OUT)  :: ErrMsg            !< Error message if ErrStat /= ErrID_None
   
   CHARACTER(*),PARAMETER     :: RoutineName="InflowWind_GetHubValues"
   INTEGER(IntKi)             :: ErrStat2
   CHARACTER(ErrMsgLen)       :: ErrMsg2            ! temporary error message
   REAL(ReKi), ALLOCATABLE    :: acc(:,:)  ! acceleration, unallocated so it won't be calculated

   ErrStat  = ErrID_None
   ErrMsg   = ""
   
   m%u_Hub%HubPosition      = InputData%HubPosition
   m%u_Hub%HubOrientation   = InputData%HubOrientation
   m%u_Hub%PositionXYZ(:,1) = InputData%HubPosition

   CALL IfW_FlowField_GetVelAcc(p%FlowField, 0, Time, m%u_Hub%PositionXYZ, &
                                m%y_Hub%VelocityUVW, acc, ErrStat2, ErrMsg2)
   CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName )
     
   Velocity = m%y_Hub%VelocityUVW(:,1)

END SUBROUTINE InflowWind_GetHubValues
!====================================================================================================
!> this routine calculates the mean wind speed 
SUBROUTINE InflowWind_GetMean( StartTime, EndTime, delta_time, InputPosition, MeanVelocity, &
                               p, x, xd, z, OtherState, m, ErrStat, ErrMsg )
!----------------------------------------------------------------------------------------------------
      ! Inputs / Outputs
   REAL(DbKi),                              INTENT(IN   )  :: StartTime         !< first time in range from which to calculate mean
   REAL(DbKi),                              INTENT(IN   )  :: EndTime           !< last time in range from which to calculate mean 
   REAL(DbKi),                              INTENT(IN   )  :: delta_time        !< dt in time range from which to calculate mean
   REAL(ReKi),                              INTENT(IN   )  :: InputPosition(3)  !< X, Y, Z positions
   REAL(ReKi),                              INTENT(  OUT)  :: MeanVelocity(3)   !< at InputPosition
      
   TYPE( InflowWind_ParameterType ),        INTENT(IN   )  :: p                 !< Parameters
   TYPE( InflowWind_ContinuousStateType ),  INTENT(IN   )  :: x                 !< Continuous states at Time
   TYPE( InflowWind_DiscreteStateType ),    INTENT(IN   )  :: xd                !< Discrete states at Time
   TYPE( InflowWind_ConstraintStateType ),  INTENT(IN   )  :: z                 !< Constraint states at Time
   TYPE( InflowWind_OtherStateType ),       INTENT(IN   )  :: OtherState        !< Other/optimization states at Time
   TYPE(InflowWind_MiscVarType),            INTENT(INOUT)  :: m                 !< misc/optimization variables
      
      
   INTEGER( IntKi ),                        INTENT(  OUT)  :: ErrStat           !< Error status of the operation
   CHARACTER(*),                            INTENT(  OUT)  :: ErrMsg            !< Error message if ErrStat /= ErrID_None


      ! local variables
   REAL(DbKi)                                         :: Time
   REAL(DbKi)                                         :: SumVel(3)
   INTEGER(IntKi)                                     :: I
   INTEGER(IntKi)                                     :: Nt
   INTEGER(IntKi)                                     :: ErrStat2
   CHARACTER(ErrMsgLen)                               :: ErrMsg2

   
   CHARACTER(*), PARAMETER                            :: RoutineName = 'InflowWind_GetMean'
      
   TYPE(InflowWind_InputType)                         :: Input                ! position where wind speed should be returned
   TYPE(InflowWind_OutputType)                        :: Output               ! velocity at InputPosition
       
       ! main body:
              
   IF ( EqualRealNos(delta_time,0.0_DbKi) ) THEN
      ErrStat = ErrID_Fatal
      ErrMsg  = RoutineName//":delta time must be non-zero."
      RETURN
   END IF
       
   ErrStat   = ErrID_None
   ErrMsg    = ""
   
   
      ! allocate arrays to compute outputs
   CALL AllocAry(Input%PositionXYZ, 3,1, 'Input%PositionXYZ',ErrStat2, ErrMsg2)
      CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName )

   CALL AllocAry(Output%VelocityUVW, 3,1, 'Output%VelocityUVW',ErrStat2, ErrMsg2)
      CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName )
      
   IF (ErrStat >= AbortErrLev) THEN
      CALL Cleanup()
      RETURN      
   END IF
              

   Nt = (EndTime - StartTime) / delta_time
   SumVel = 0.0
   Input%PositionXYZ(:,1) = InputPosition      

   DO I=1,Nt

      Time = StartTime + (I-1)*delta_time

      CALL CalculateOutput( Time, Input, p, x, xd, z, OtherState, Output, m, .FALSE., ErrStat2, ErrMsg2 )      
         CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName )                
         
      IF ( ErrStat >= AbortErrLev ) THEN
         MeanVelocity = SumVel / REAL(I-1, ReKi)
         CALL Cleanup()
         RETURN
      ELSE
         SumVel = SumVel + Output%VelocityUVW(:,1)
      END IF

   END DO

   MeanVelocity = SumVel / REAL(Nt, ReKi)
   CALL Cleanup()
   RETURN
   
CONTAINS
!............................
   SUBROUTINE Cleanup()
      
      IF (ALLOCATED(Input%PositionXYZ)) DEALLOCATE(Input%PositionXYZ)
      IF (ALLOCATED(Output%VelocityUVW)) DEALLOCATE(Output%VelocityUVW)
   
   END SUBROUTINE Cleanup   
!............................
END SUBROUTINE InflowWind_GetMean
!**********************************************************************************************************************************
END MODULE InflowWind_Subs
